#include <string>
#include <fstream>
#include <iostream>
#include <iterator>


#include <Wt/WResource>
#include <Wt/WJavaScript>
#include <Wt/WApplication>
#include <Wt/WPaintDevice>
#include <Wt/Http/Request>
#include <Wt/Http/Response>
#include <Wt/WPaintedWidget>
#include <Wt/WContainerWidget>
#include <Wt/WCssDecorationStyle>
#include <Wt/Chart/WAbstractChart>


// Disable streamsize <=> size_t warnings in boost
#pragma warning(disable:4244)

#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

#include "genetic_lego/FileDragUploadResource.h"
#include "js/FileDragUploadResource.js"


using namespace Wt;
using namespace std;

/*
 * See also: http://www.webtoolkit.eu/wt/blog/2010/03/02/javascript_that_is_c__
 */
#define INLINE_JAVASCRIPT(...) #__VA_ARGS__



FileDragUploadResource::FileDragUploadResource( WObject *parent  )
    : WResource( parent ),
      m_fileDrop( NULL )
{
  m_fileDrop = new Wt::Signal<std::string, std::string>( this );
}


FileDragUploadResource::~FileDragUploadResource()
{
  beingDeleted();
  for( size_t i = 0; i < m_spooledFiles.size(); ++i )
  {
    bool success = false;
    try{
      success = boost::filesystem::remove( m_spooledFiles[i] );
    }catch(...){}
    if( !success )
      cerr << "Warning, could not delete file " << m_spooledFiles[i] << endl;
  }
}


Wt::Signal<std::string,std::string > *FileDragUploadResource::fileDrop() //<display_name,spool_name>
{
  return m_fileDrop;
}


void FileDragUploadResource::addDragNDropToWidget( Wt::WWebWidget *widget )
{
  WApplication *app = WApplication::instance();

  LOAD_JAVASCRIPT(app, "js/FileDragUploadResource.js", "FileDragUploadResource", wtjsHandleDragOver );
  LOAD_JAVASCRIPT(app, "js/FileDragUploadResource.js", "FileDragUploadResource", wtjsHandleDragExit );
  LOAD_JAVASCRIPT(app, "js/FileDragUploadResource.js", "FileDragUploadResource", wtjsHandleDragOver );
  LOAD_JAVASCRIPT(app, "js/FileDragUploadResource.js", "FileDragUploadResource", wtjsHandleDrop );
  LOAD_JAVASCRIPT(app, "js/FileDragUploadResource.js", "FileDragUploadResource", wtjsInitDragNDrop );

  //Drag-n-drop support of WPaintedWidgets requires adding the signals to the
  //  canvas, not whatever widget holds the canvas
  WPaintedWidget *painted = dynamic_cast<WPaintedWidget *>( widget );
  const string extra_char = (painted ? "c" : "" );

  const string id = extra_char + widget->id();
  app->doJavaScript( "Wt.WT.InitDragNDrop('" + id + "','" + url() + "');" );
}//void addDragNDropToWidget( Wt::WWebWidget *widget )


void FileDragUploadResource::handleRequest( const Http::Request& request,
                                            Http::Response& response )
{
//XXX - It's assuming handleRequest(...) is only called once all the data
//      is uploaded.
//XXX - Need to deal with case if the data was spooled to file ? - maybe not?
//    std::vector<Http::UploadedFile> files;
//    std::pair<Http::UploadedFileMap::const_iterator, Http::UploadedFileMap::const_iterator> range
//               = request.uploadedFiles().equal_range(key);
//    for(Http::UploadedFileMap::const_iterator i = range.first; i != range.second; ++i)
//      files.push_back(i->second);
//    for (unsigned i = 0; i < files.size(); ++i)
//      if (!files[i].clientFileName().empty())
//      {
//        m_spooledFiles.push_back( files[i].spoolFileName() );
//        m_dragCanvas->fileDrop().emit( files[i].clientFileName(), files[i].spoolFileName() );
//      }

  response.setMimeType("text/html; charset=utf-8");
  response.addHeader("Expires", "Sun, 14 Jun 2020 00:00:00 GMT");
  response.addHeader("Cache-Control", "max-age=315360000");

  std::ostream &output = response.out();

  if( request.tooLarge() )
  {
    response.setStatus( 413 );
    const int64_t max_size = wApp->maximumRequestSize();
    output << "Sorry, the maximum uplaod size is " << max_size /1024 << " kb";
    return;
  }//if( request.tooLarge() )

  const int datalen = request.contentLength();

  if( datalen )
  {
    using boost::filesystem::path;
    using boost::filesystem::unique_path;

    boost::filesystem::path temppath = boost::filesystem::temp_directory_path();
    temppath /= ("genetic_lego_%%%%-%%%%-%%%%-%%%%");  //wApp->sessionId()
    temppath = boost::filesystem::unique_path( temppath );

    ofstream spool_file( temppath.c_str() );

    if( spool_file.is_open() )
    {
      spool_file << request.in().rdbuf();
      spool_file.close();
      const string userName = request.headerValue( "X-File-Name" );

      m_spooledFiles.push_back( temppath );
      if( m_fileDrop )
        m_fileDrop->emit( userName, temppath.generic_string() );
    } else
    {
      response.setStatus( 500 );  //Internal Server error
      output << "Coulnt open temporary file on server";
    }//if( spool_file.is_open() ) / else
  }//if( request.contentLength() )

}//void FileDragUploadResource::handleRequest(...)
